package com.wxd.woff.table.glyf;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.wxd.woff.AbstractOTFDataStream;

/**
 * @Description: 功能描述：Composite Glyph Description
 *
 * @ClassName: GlyfCompositeDescript
 * @author: Wang Xindong
 * @date: 2018年9月5日 下午3:00:52
 * @email: wangxindong@travelsky.com
 * @Copyright: Copyright (c) 2018. All rights reserved.
 */
public class GlyfCompositeDescript extends AbstractGlyfDescript {

	private GlyphTable glyphTable = null;
	private boolean beingResolved = false;
	private boolean resolved = false;
	private int pointCount = -1;
	private int contourCount = -1;

	private List<GlyfCompositeComp> components = new ArrayList<>(8);
	private final Map<Integer, GlyphDescription> descriptions = new HashMap<>();

	/**
	 * 复合字形
	 */
	public GlyfCompositeDescript(AbstractOTFDataStream bais, GlyphTable glyphTable) throws IOException {
		super((short) -1, bais);
		this.glyphTable = glyphTable;
		// Get all of the composite components
		GlyfCompositeComp comp;
		do {
			comp = new GlyfCompositeComp(bais);
			components.add(comp);
		} while ((comp.getFlags() & GlyfCompositeComp.MORE_COMPONENTS) != 0);
		if ((comp.getFlags() & GlyfCompositeComp.WE_HAVE_INSTRUCTIONS) != 0) {
			readInstructions(bais, (bais.readUnsignedShort()));
		}
		initDescriptions();
	}

	private void initDescriptions() {
		for (GlyfCompositeComp component : components) {
			try {
				int index = component.getGlyphIndex();
				GlyphData glyph = glyphTable.getGlyph(index);
				if (glyph != null) {
					descriptions.put(index, glyph.getDescription());
				}
			} catch (IOException e) {
			}
		}
	}

	@Override
	public void resolve() {
		if (resolved) {
			return;
		}
		if (beingResolved) {
			LOGGER.error("Circular reference in GlyfCompositeDesc");
			return;
		}
		beingResolved = true;

		int firstIndex = 0;
		int firstContour = 0;

		for (GlyfCompositeComp comp : components) {
			comp.setFirstIndex(firstIndex);
			comp.setFirstContour(firstContour);

			GlyphDescription desc = descriptions.get(comp.getGlyphIndex());
			if (desc != null) {
				desc.resolve();
				firstIndex += desc.getPointCount();
				firstContour += desc.getContourCount();
			}
		}
		resolved = true;
		beingResolved = false;
	}

	@Override
	public int getEndPtOfContours(int i) {
		GlyfCompositeComp c = getCompositeCompEndPt(i);
		if (c != null) {
			GlyphDescription gd = descriptions.get(c.getGlyphIndex());
			return gd.getEndPtOfContours(i - c.getFirstContour()) + c.getFirstIndex();
		}
		return 0;
	}

	private GlyfCompositeComp getCompositeCompEndPt(int i) {
		for (GlyfCompositeComp c : components) {
			GlyphDescription gd = descriptions.get(c.getGlyphIndex());
			if (c.getFirstContour() <= i && gd != null && i < (c.getFirstContour() + gd.getContourCount())) {
				return c;
			}
		}
		return null;
	}

	@Override
	public byte getFlags(int i) {
		GlyfCompositeComp c = getCompositeComp(i);
		if (c != null) {
			GlyphDescription gd = descriptions.get(c.getGlyphIndex());
			return gd.getFlags(i - c.getFirstIndex());
		}
		return 0;
	}

	@Override
	public short getXCoordinate(int i) {
		GlyfCompositeComp c = getCompositeComp(i);
		if (c != null) {
			GlyphDescription gd = descriptions.get(c.getGlyphIndex());
			int n = i - c.getFirstIndex();
			int x = gd.getXCoordinate(n);
			int y = gd.getYCoordinate(n);
			short x1 = (short) c.scaleX(x, y);
			x1 += c.getXtranslate();
			return x1;
		}
		return 0;
	}

	@Override
	public short getYCoordinate(int i) {
		GlyfCompositeComp c = getCompositeComp(i);
		if (c != null) {
			GlyphDescription gd = descriptions.get(c.getGlyphIndex());
			int n = i - c.getFirstIndex();
			int x = gd.getXCoordinate(n);
			int y = gd.getYCoordinate(n);
			short y1 = (short) c.scaleY(x, y);
			y1 += c.getYtranslate();
			return y1;
		}
		return 0;
	}

	@Override
	public boolean isComposite() {
		return true;
	}

	@Override
	public int getPointCount() {
		if (!resolved) {
			LOGGER.error("getPointCount called on unresolved GlyfCompositeDescript");
		}
		if (pointCount < 0) {
			GlyfCompositeComp c = components.get(components.size() - 1);
			GlyphDescription gd = descriptions.get(c.getGlyphIndex());
			if (gd == null) {
				LOGGER.error("getGlypDescription(" + c.getGlyphIndex() + ") is null, returning 0");
				pointCount = 0;
			} else {
				pointCount = c.getFirstIndex() + gd.getPointCount();
			}
		}
		return pointCount;
	}

	public int getComponentCount() {
		return components.size();
	}

	public int getContourCount() {
		if (!resolved) {
			LOGGER.error("getContourCount called on unresolved GlyfCompositeDescript");
		}
		if (contourCount < 0) {
			GlyfCompositeComp c = components.get(components.size() - 1);
			contourCount = c.getFirstContour() + descriptions.get(c.getGlyphIndex()).getContourCount();
		}
		return contourCount;
	}

	private GlyfCompositeComp getCompositeComp(int i) {
		for (GlyfCompositeComp c : components) {
			GlyphDescription gd = descriptions.get(c.getGlyphIndex());
			if (c.getFirstIndex() <= i && gd != null && i < (c.getFirstIndex() + gd.getPointCount())) {
				return c;
			}
		}
		return null;
	}

	@Override
	public String toString() {
		return "GlyfCompositeDescript [glyphTable=" + glyphTable + ", beingResolved=" + beingResolved + ", resolved="
				+ resolved + ", pointCount=" + pointCount + ", contourCount=" + contourCount + ", components="
				+ components + ", descriptions=" + descriptions + "]";
	}

	@Override
	public Point[] getPoint() {
		GlyphRenderer renderer = new GlyphRenderer(this);
		return renderer.describe();
	}

}
